/*
 * Copyright (c) [2022] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "x64_MPISel.h"
#include "x64_memlayout.h"
#include "x64_cgfunc.h"
#include "x64_isa_tbl.h"
#include "x64_call_conv.h"
#include "x64_cg.h"
#include "isel.h"

namespace maplebe {
/* Field-ID 0 is assigned to the top level structure. (Field-ID also defaults to 0 if it is not a structure.) */
CGMemOperand &X64MPIsel::GetOrCreateMemOpndFromSymbol(const MIRSymbol &symbol, FieldID fieldId) {
  MIRStorageClass storageClass = symbol.GetStorageClass();
  CGMemOperand *result = nullptr;
  CGRegOperand *stackBaseReg = nullptr;
  if ((storageClass == kScAuto) || (storageClass == kScFormal)) {
    auto *symloc = static_cast<X64SymbolAlloc*>(cgFunc->GetMemlayout()->GetSymAllocInfo(symbol.GetStIndex()));
    ASSERT(symloc != nullptr, "sym loc should have been defined");
    int stOfst = cgFunc->GetBaseOffset(*symloc);
    stackBaseReg = static_cast<X64CGFunc*>(cgFunc)->GetBaseReg(*symloc);
    /* Create field symbols in aggregate structure */
    if (symbol.GetType()->GetPrimType() == PTY_agg) {
      MIRStructType *structType = static_cast<MIRStructType*>(symbol.GetType());
      ASSERT(structType != nullptr, "non-structure");
      PrimType symType = structType->GetFieldType(fieldId)->GetPrimType();
      int32 fieldOffset = static_cast<uint32>(cgFunc->GetBecommon().GetFieldOffset(*structType, fieldId).first);
      result = &GetCurFunc()->GetOpndBuilder()->CreateMem(GetPrimTypeBitSize(symType));
      result->SetBaseRegister(*stackBaseReg);
      result->SetBaseOfst(GetCurFunc()->GetOpndBuilder()->CreateImm(stackBaseReg->GetSize(), stOfst + fieldOffset));
    } else {
      uint32 opndSz = GetPrimTypeSize(symbol.GetType()->GetPrimType()) * kBitsPerByte;
      result = &GetCurFunc()->GetOpndBuilder()->CreateMem(opndSz);
      result->SetBaseRegister(*stackBaseReg);
      result->SetBaseOfst(GetCurFunc()->GetOpndBuilder()->CreateImm(stackBaseReg->GetSize(), stOfst));
    }
    CHECK_FATAL(result != nullptr, "NIY");
    return *result;
  }
  stackBaseReg = &GetCurFunc()->GetOpndBuilder()->CreatePReg(x64::RIP, k64BitSize, kRegTyInt);
  if ((storageClass == kScGlobal) || (storageClass == kScExtern)) {
    int32 fieldOffset = 0;
    PrimType symType = symbol.GetType()->GetPrimType();
    uint32 dataSize = GetPrimTypeBitSize(symType);
    if (fieldId != 0) {
      MIRStructType *structType = static_cast<MIRStructType*>(symbol.GetType());
      ASSERT(structType != nullptr, "SelectDassign: non-zero fieldID for non-structure");
      symType = static_cast<MIRStructType*>(symbol.GetType())->GetFieldType(fieldId)->GetPrimType();
      dataSize = GetPrimTypeBitSize(symType);
      fieldOffset = static_cast<uint32>(cgFunc->GetBecommon().GetFieldOffset(*structType, fieldId).first);
    }
    result = &GetCurFunc()->GetOpndBuilder()->CreateMem(dataSize);
    CGImmOperand &stOfstOpnd = GetCurFunc()->GetOpndBuilder()->CreateImm(symbol, fieldOffset, 0);
    result->SetBaseRegister(*stackBaseReg);
    result->SetBaseOfst(stOfstOpnd);
    CHECK_FATAL(result != nullptr, "NIY");
    return *result;
  }
  if ((storageClass == kScPstatic) || (storageClass == kScFstatic)) {
    CHECK_FATAL(false, "NIY");
  }
  CHECK_FATAL(false, "NIY");
  return *result;
}

void X64MPIsel::SelectReturn(Operand &opnd) {
  X64CallConvImpl retLocator(cgFunc->GetBecommon());
  CCLocInfo retMech;
  MIRType *retType = cgFunc->GetFunction().GetReturnType();
  retLocator.LocateRetVal(*retType, retMech);
  regno_t retReg = retMech.GetReg0();
  if (retMech.GetRegCount() > 0) {
    if (opnd.IsRegister()) {
      CGRegOperand *regOpnd = static_cast<CGRegOperand*>(&opnd);
      PrimType oriPrimType = retMech.GetPrimTypeOfReg0();
      CGRegOperand &retOpnd = cgFunc->GetOpndBuilder()->CreatePReg(retReg, regOpnd->GetSize(),
          cgFunc->GetRegTyFromPrimTy(oriPrimType));
      SelectCopy(retOpnd, opnd, oriPrimType);
    } else if (opnd.IsConstImmediate()) {
      PrimType oriPrimType = retMech.GetPrimTypeOfReg0();
      CGRegOperand &retOpnd = cgFunc->GetOpndBuilder()->CreatePReg(retReg, GetPrimTypeBitSize(oriPrimType),
          cgFunc->GetRegTyFromPrimTy(oriPrimType));
      SelectCopy(retOpnd, opnd, oriPrimType);
      return;
    } else {
      CHECK_FATAL(false, "NIY");
    }
  }
  cgFunc->GetExitBBsVec().emplace_back(cgFunc->GetCurBB());
}

/*
   SelectParmList generates an instrunction for each of the parameters
   to load the parameter value into the corresponding register.
   We return a list of registers to the call instruction because
   they may be needed in the register allocation phase.
 */
void X64MPIsel::SelectParmList(StmtNode &naryNode, CGListOperand &srcOpnds) {
  size_t i = 0;

  X64CallConvImpl parmLocator(cgFunc->GetBecommon());
  CCLocInfo ploc;
  for (uint32 pnum = 0; i < naryNode.NumOpnds(); ++i, ++pnum) {
    MIRType *ty = nullptr;
    BaseNode *argExpr = naryNode.Opnd(i);
    PrimType primType = argExpr->GetPrimType();
    ASSERT(primType != PTY_void, "primType should not be void");
    auto calleePuIdx = static_cast<CallNode&>(naryNode).GetPUIdx();
    auto *callee = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(calleePuIdx);

    ty = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<uint32>(primType)];
    CGRegOperand *expRegOpnd = nullptr;
    Operand *opnd = HandleExpr(naryNode, *argExpr);
    if (!opnd->IsRegister()) {
      opnd = &SelectCopy2Reg(*opnd, primType);
    }
    expRegOpnd = static_cast<CGRegOperand*>(opnd);

    parmLocator.LocateNextParm(*ty, ploc);

    /* skip unused args */
    if (callee != nullptr && callee->GetFuncDesc().IsArgUnused(pnum)) continue;

    if (ploc.reg0 != x64::kRinvalid) {  /* load to the register. */
      CHECK_FATAL(expRegOpnd != nullptr, "null ptr check");
      CGRegOperand &parmRegOpnd = cgFunc->GetOpndBuilder()->CreatePReg(ploc.reg0, expRegOpnd->GetSize(),
          cgFunc->GetRegTyFromPrimTy(primType));
      SelectCopy(parmRegOpnd, *opnd, primType);
      srcOpnds.PushOpnd(parmRegOpnd);
    } else {
      MOperator mOp = x64::MOP_movl_r_m;
      CGRegOperand &baseOpnd = cgFunc->GetOpndBuilder()->CreatePReg(x64::RSP, k64BitSize,
          cgFunc->GetRegTyFromPrimTy(primType));
      CGMemOperand &actMemOpnd = cgFunc->GetOpndBuilder()->CreateMem(baseOpnd, ploc.memOffset,
          GetPrimTypeBitSize(primType));
      Insn &addrInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
      addrInsn.AddOperandChain(*expRegOpnd).AddOperandChain(actMemOpnd);
      cgFunc->GetCurBB()->AppendInsn(addrInsn);
  }
    ASSERT(ploc.reg1 == 0, "SelectCall NYI");
  }
}

Insn &X64MPIsel::AppendCall(MIRSymbol &sym, CGListOperand &srcOpnds) {
  Operand &targetOpnd = cgFunc->GetOpndBuilder()->CreateFuncNameOpnd(sym);
  x64::X64MOP_t mop = x64::MOP_callq_l;
  Insn *callInsn = nullptr;
  callInsn = &(cgFunc->GetInsnBuilder()->BuildInsn(mop, X64CG::kMd[mop]));
  callInsn->AddOperandChain(targetOpnd).AddOperandChain(srcOpnds);
  cgFunc->GetCurBB()->AppendInsn(*callInsn);
  cgFunc->GetCurBB()->SetHasCall();
  return *callInsn;
}

void X64MPIsel::SelectCall(CallNode &callNode) {
  MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode.GetPUIdx());
  MIRSymbol *fsym = cgFunc->GetFunction().GetLocalOrGlobalSymbol(fn->GetStIdx(), false);
  MIRType *retType = fn->GetReturnType();
  CGListOperand &srcOpnds = cgFunc->GetOpndBuilder()->CreateList();
  SelectParmList(callNode, srcOpnds);
  Insn &callInsn = AppendCall(*fsym, srcOpnds);
  if (retType != nullptr) {
    callInsn.SetRetSize(static_cast<uint32>(retType->GetSize()));
    callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(retType->GetPrimType()));
  }
  cgFunc->GetCurBB()->SetHasCall();
  cgFunc->GetFunction().SetHasCall();
}

Operand &X64MPIsel::ProcessReturnReg(PrimType primType, int32 sReg) {
  return GetTargetRetOperand(primType, sReg);
}

void X64MPIsel::SelectGoto(GotoNode &stmt) {
  MOperator mOp = x64::MOP_jmpq_l;
  auto funcName = ".L." + std::to_string(cgFunc->GetUniqueID()) + "__" + std::to_string(stmt.GetOffset());
  CGLabelOperand &targetOpnd = cgFunc->GetOpndBuilder()->CreateLabel(funcName.c_str(), stmt.GetOffset());
  Insn &jmpInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
  cgFunc->GetCurBB()->AppendInsn(jmpInsn);
  jmpInsn.AddOperandChain(targetOpnd);
  cgFunc->GetCurBB()->SetKind(BB::kBBGoto);
  return;
}

void X64MPIsel::SelectRangeGoto(RangeGotoNode &rangeGotoNode, Operand &srcOpnd) {
  MIRType *etype = GlobalTables::GetTypeTable().GetTypeFromTyIdx((TyIdx)PTY_a64);
  std::vector<uint32> sizeArray;
  const SmallCaseVector &switchTable = rangeGotoNode.GetRangeGotoTable();
  sizeArray.emplace_back(switchTable.size());
  MemPool *memPool = cgFunc->GetMemoryPool();
  MIRArrayType *arrayType = memPool->New<MIRArrayType>(etype->GetTypeIndex(), sizeArray);
  MIRAggConst *arrayConst = memPool->New<MIRAggConst>(cgFunc->GetMirModule(), *arrayType);
  for (const auto &itPair : switchTable) {
    LabelIdx labelIdx = itPair.second;
    cgFunc->GetCurBB()->PushBackRangeGotoLabel(labelIdx);
    MIRConst *mirConst = memPool->New<MIRLblConst>(labelIdx, cgFunc->GetFunction().GetPuidx(), *etype);
    arrayConst->AddItem(mirConst, 0);
  }
  MIRSymbol *lblSt = cgFunc->GetFunction().GetSymTab()->CreateSymbol(kScopeLocal);
  lblSt->SetStorageClass(kScFstatic);
  lblSt->SetSKind(kStConst);
  lblSt->SetTyIdx(arrayType->GetTypeIndex());
  lblSt->SetKonst(arrayConst);
  std::string lblStr(".L_");
  uint32 labelIdxTmp = cgFunc->GetLabelIdx();
  lblStr.append(std::to_string(cgFunc->GetUniqueID())).append("_LOCAL_CONST.").append(std::to_string(labelIdxTmp++));
  cgFunc->SetLabelIdx(labelIdxTmp);
  lblSt->SetNameStrIdx(lblStr);
  cgFunc->AddEmitSt(cgFunc->GetCurBB()->GetId(), *lblSt);
  CGImmOperand &stOpnd = cgFunc->GetOpndBuilder()->CreateImm(*lblSt, 0, 0);
  /* get index */
  PrimType srcType = rangeGotoNode.Opnd(0)->GetPrimType();
  CGRegOperand &opnd0 = SelectCopy2Reg(srcOpnd, srcType);
  int32 minIdx = switchTable[0].first;
  CGImmOperand &opnd1 = cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(srcType),
                                                            -minIdx - rangeGotoNode.GetTagOffset());
  CGRegOperand &indexOpnd = cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(srcType), kRegTyInt);
  SelectAdd(indexOpnd, opnd0, opnd1, srcType);

  /* load the displacement into a register by accessing memory at base + index * 8 */
  /* mov .L_xxx_LOCAL_CONST.x(%baseReg, %indexOpnd, 8), %dstRegOpnd */
  CGMemOperand &dstMemOpnd = cgFunc->GetOpndBuilder()->CreateMem(GetPrimTypeBitSize(PTY_a64));
  CGRegOperand &baseReg = cgFunc->GetOpndBuilder()->CreatePReg(x64::RBP, GetPrimTypeBitSize(PTY_i64), kRegTyInt);
  dstMemOpnd.SetBaseRegister(baseReg);
  dstMemOpnd.SetIndexRegister(indexOpnd);
  dstMemOpnd.SetBaseOfst(stOpnd);
  dstMemOpnd.SetScaleFactor(cgFunc->GetOpndBuilder()->CreateImm(baseReg.GetSize(), k8ByteSize));
  CGRegOperand &dstRegOpnd = SelectCopy2Reg(dstMemOpnd, PTY_a64);

  /* jumping to the absolute address which is stored in dstRegOpnd */
  MOperator mOp = x64::MOP_jmpq_r;
  Insn &jmpInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
  jmpInsn.AddOperandChain(dstRegOpnd);
  cgFunc->GetCurBB()->AppendInsn(jmpInsn);
}

Operand *X64MPIsel::SelectAddrof(AddrofNode &expr, const BaseNode &parent) {
  PrimType ptype = expr.GetPrimType();
  CGRegOperand &resReg = cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(ptype),
      cgFunc->GetRegTyFromPrimTy(ptype));
  MIRSymbol *symbol = cgFunc->GetFunction().GetLocalOrGlobalSymbol(expr.GetStIdx());
  MIRStorageClass storageClass = symbol->GetStorageClass();
  MOperator mOp;
  if ((storageClass == kScAuto) || (storageClass == kScFormal)) {
    CGMemOperand &memOperand = GetOrCreateMemOpndFromSymbol(*symbol, expr.GetFieldID());
    uint srcByteSize = GetPrimTypeSize(symbol->GetType()->GetPrimType());
    if (srcByteSize <= k4ByteSize) {
      mOp = x64::MOP_leal_m_r;
    } else if (srcByteSize <= k8ByteSize) {
      mOp = x64::MOP_leaq_m_r;
    } else {
      CHECK_FATAL(false, "NIY");
    }
    Insn &addrInsn = (cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]));
    addrInsn.AddOperandChain(memOperand).AddOperandChain(resReg);
    cgFunc->GetCurBB()->AppendInsn(addrInsn);
  } else if ((storageClass == maple::kScFstatic) || (storageClass == maple::kScPstatic) ||
      (storageClass == kScGlobal) || (storageClass == kScExtern)) {
    mOp = x64::MOP_movabs_l_r;
    const char *symbolName = symbol->GetName().c_str();
    LabelIdx idx = cgFunc->CreateLabel();
    CGLabelOperand &labelOpnd = cgFunc->GetOpndBuilder()->CreateLabel(symbolName, idx);
    Insn &addrInsn = (cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]));
    addrInsn.AddOperandChain(labelOpnd).AddOperandChain(resReg);
    cgFunc->GetCurBB()->AppendInsn(addrInsn);
  }
  return &resReg;
}

MOperator X64MPIsel::PickJmpInsn(Opcode brOp, Opcode cmpOp, bool isSigned) const {
  switch (cmpOp) {
    case OP_ne:
      return (brOp == OP_brtrue) ? MOP_jne_l : MOP_je_l;
    case OP_eq:
      return (brOp == OP_brtrue) ? MOP_je_l : MOP_jne_l;
    case OP_lt:
      return (brOp == OP_brtrue) ? (isSigned ? MOP_jl_l  : MOP_jb_l)
                                 : (isSigned ? MOP_jge_l : MOP_jbe_l);
    case OP_le:
      return (brOp == OP_brtrue) ? (isSigned ? MOP_jle_l : MOP_jbe_l)
                                 : (isSigned ? MOP_jg_l  : MOP_ja_l);
    case OP_gt:
      return (brOp == OP_brtrue) ? (isSigned ? MOP_jg_l  : MOP_ja_l)
                                 : (isSigned ? MOP_jle_l : MOP_jbe_l);
    case OP_ge:
      return (brOp == OP_brtrue) ? (isSigned ? MOP_jge_l : MOP_jae_l)
                                 : (isSigned ? MOP_jl_l  : MOP_jb_l);
    default:
      CHECK_FATAL(false, "PickJmpInsn error");
  }
}

/*
 * handle brfalse/brtrue op, opnd0 can be a compare node or non-compare node
 * such as a dread for example
 */
void X64MPIsel::SelectCondGoto(CondGotoNode &stmt, BaseNode &condNode, Operand &opnd0) {
  /* gen targetOpnd, .L.xxx__xx */
  auto funcName = ".L." + std::to_string(cgFunc->GetUniqueID()) + "__" + std::to_string(stmt.GetOffset());
  CGLabelOperand &targetOpnd = cgFunc->GetOpndBuilder()->CreateLabel(funcName.c_str(), stmt.GetOffset());
  bool isCompareNode = kOpcodeInfo.IsCompare(condNode.GetOpCode());
  Opcode cmpOp = isCompareNode ? condNode.GetOpCode() : OP_ne;
  PrimType pType = isCompareNode ? static_cast<CompareNode&>(condNode).GetOpndType() : condNode.GetPrimType();
  if (IsPrimitiveFloat(pType)) {
    CHECK_FATAL(false, "unsupported type");
  }
  bool isSigned = IsSignedInteger(pType);
  /* select jump Insn */
  MOperator jmpOperator = PickJmpInsn(stmt.GetOpCode(), cmpOp, isSigned);
  Insn &jmpInsn = (cgFunc->GetInsnBuilder()->BuildInsn(jmpOperator, X64CG::kMd[jmpOperator]));
  jmpInsn.AddOperandChain(targetOpnd);
  cgFunc->GetCurBB()->AppendInsn(jmpInsn);
}

Operand *X64MPIsel::SelectStrLiteral(ConststrNode &constStr) {
  std::string labelStr;
  labelStr.append(".LUstr_");
  labelStr.append(std::to_string(constStr.GetStrIdx()));
  MIRSymbol *labelSym = GlobalTables::GetGsymTable().GetSymbolFromStrIdx(
      GlobalTables::GetStrTable().GetStrIdxFromName(labelStr));
  MIRType *etype = GlobalTables::GetTypeTable().GetTypeFromTyIdx((TyIdx)PTY_a64);
  auto *c = cgFunc->GetMemoryPool()->New<MIRStrConst>(constStr.GetStrIdx(), *etype);
  if (labelSym == nullptr) {
    labelSym = cgFunc->GetMirModule().GetMIRBuilder()->CreateGlobalDecl(labelStr, c->GetType());
    labelSym->SetStorageClass(kScFstatic);
    labelSym->SetSKind(kStConst);
    /* c may be local, we need a global node here */
    labelSym->SetKonst(cgFunc->NewMirConst(*c));
  }
  if (c->GetPrimType() == PTY_ptr) {
    CGImmOperand &stOpnd = cgFunc->GetOpndBuilder()->CreateImm(*labelSym, 0, 0);
    CGRegOperand &addrOpnd = cgFunc->GetOpndBuilder()->CreateVReg(k64BitSize, cgFunc->GetRegTyFromPrimTy(PTY_a64));
    Insn &addrOfInsn = (cgFunc->GetInsnBuilder()->BuildInsn(x64::MOP_leaq_m_r, X64CG::kMd[x64::MOP_leaq_m_r]));
    addrOfInsn.AddOperandChain(stOpnd).AddOperandChain(addrOpnd);
    cgFunc->GetCurBB()->AppendInsn(addrOfInsn);
    return &addrOpnd;
  }
  CHECK_FATAL(false, "Unsupported const string type");
  return nullptr;
}

Operand &X64MPIsel::GetTargetRetOperand(PrimType primType, int32 sReg) {
  X64CallConvImpl retLocator(cgFunc->GetBecommon());
  CCLocInfo retMech;
  MIRType *retType = cgFunc->GetFunction().GetReturnType();
  retLocator.LocateRetVal(*retType, retMech);
  uint32 bitSize = GetPrimTypeBitSize(primType);
  if (sReg < 0) {
    CHECK_FATAL(false, "NIY");
  } else {
    regno_t retReg = retMech.GetReg0();
    CGRegOperand &parmRegOpnd = cgFunc->GetOpndBuilder()->CreatePReg(retReg, bitSize,
        cgFunc->GetRegTyFromPrimTy(primType));
    return parmRegOpnd;
  }
}

Operand *X64MPIsel::SelectMpy(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent) {
  PrimType dtype = node.GetPrimType();
  CGRegOperand *resOpnd = nullptr;
  if (!IsPrimitiveVector(dtype)) {
    resOpnd = &cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(dtype),
        cgFunc->GetRegTyFromPrimTy(dtype));
    SelectMpy(*resOpnd, opnd0, opnd1, dtype);
  } else {
    /* vector operand */
    CHECK_FATAL(false, "NIY");
  }

  return resOpnd;
}

void X64MPIsel::SelectMpy(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType) {
  if (IsSignedInteger(primType) || IsUnsignedInteger(primType)) {
    uint32 bitSize = GetPrimTypeBitSize(primType);
    SelectCopy(resOpnd, opnd0, primType);
    CGRegOperand &regOpnd1 = SelectCopy2Reg(opnd1, primType);
    X64MOP_t mOp = (bitSize == k64BitSize) ? x64::MOP_imulq_r_r :
        (bitSize == k32BitSize) ? x64::MOP_imull_r_r : (bitSize == k16BitSize) ? x64::MOP_imulw_r_r : x64::MOP_begin;
    CHECK_FATAL(mOp != x64::MOP_begin, "NIY mapping");
    Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
    insn.AddOperandChain(regOpnd1).AddOperandChain(resOpnd);
    cgFunc->GetCurBB()->AppendInsn(insn);
  } else {
    CHECK_FATAL(false, "NIY");
  }
}

/*
 *  Dividend(EDX:EAX) / Divisor(reg/mem32) = Quotient(EAX)     Remainder(EDX)
 *  IDIV instruction perform signed division of EDX:EAX by the contents of 32-bit register or memory location and
 *  store the quotient in EAX and the remainder in EDX.
 *  The instruction truncates non-integral results towards 0. The sign of the remainder is always the same as the sign
 *  of the dividend, and the absolute value of the remainder is less than the absolute value of the divisor.
 *  An overflow generates a #DE (divide error) exception, rather than setting the OF flag.
 *  To avoid overflow problems, precede this instruction with a CDQ instruction to sign-extend the dividend Divisor.
 *  CDQ Sign-extend EAX into EDX:EAX. This action helps avoid overflow problems in signed number arithmetic.
 */
Operand *X64MPIsel::SelectDiv(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent) {
  PrimType dtype = node.GetPrimType();
  bool isSigned = IsSignedInteger(dtype);
  uint32 dsize = GetPrimTypeBitSize(dtype);
  bool is64Bits = (dsize == k64BitSize);
  bool isFloat = IsPrimitiveFloat(dtype);
  PrimType primType =
      isFloat ? dtype : ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
  CGRegOperand *resOpnd = &cgFunc->GetOpndBuilder()->CreatePReg(x64::RAX, GetPrimTypeBitSize(dtype),
      cgFunc->GetRegTyFromPrimTy(primType));
  MOperator mOp = x64::MOP_idiv_r;
  CGRegOperand *rdxOpnd = &cgFunc->GetOpndBuilder()->CreatePReg(x64::RDX, GetPrimTypeBitSize(dtype),
      cgFunc->GetRegTyFromPrimTy(primType));
  SelectCopy(*resOpnd, opnd0, primType);
  Insn &cdqInsn = cgFunc->GetInsnBuilder()->BuildInsn(x64::MOP_cdq, X64CG::kMd[x64::MOP_cdq]);
  cdqInsn.AddOperandChain(*resOpnd).AddOperandChain(*rdxOpnd);
  cgFunc->GetCurBB()->AppendInsn(cdqInsn);
  Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
  insn.AddOperandChain(opnd1).AddOperandChain(*rdxOpnd);
  cgFunc->GetCurBB()->AppendInsn(insn);
  return resOpnd;
}

Operand *X64MPIsel::SelectCmpOp(CompareNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent) {
  PrimType dtype = node.GetPrimType();
  CGRegOperand *resOpnd = nullptr;
  if (!IsPrimitiveVector(node.GetPrimType())) {
    resOpnd = &cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(dtype),
        cgFunc->GetRegTyFromPrimTy(dtype));
    SelectCmpOp(*resOpnd, opnd0, opnd1, node.GetOpCode(), dtype, node.GetOpndType(), parent);
  } else {
    /* vector operand */
    CHECK_FATAL(false, "NIY");
  }
  return resOpnd;
}

void X64MPIsel::SelectCmpOp(CGRegOperand &resOpnd, Operand &opnd0, Operand &opnd1, Opcode opCode, PrimType primType,
                            PrimType primOpndType, const BaseNode &parent) {
  bool isFloat = IsPrimitiveFloat(primOpndType);
  bool isSigned = !IsPrimitiveUnsigned(primOpndType);
  CGRegOperand &regOpnd0 = SelectCopy2Reg(opnd0, primOpndType);
  CGRegOperand &regOpnd1 = SelectCopy2Reg(opnd1, primOpndType);

  /* cmp */
  if (isFloat) {
    CHECK_FATAL(false, "NIY");
  } else {
    x64::X64MOP_t cmpMOp = GetCmpMop(regOpnd0.GetKind(), primOpndType, regOpnd1.GetKind(), primOpndType);
    ASSERT(cmpMOp != x64::MOP_begin, "unsupported mOp");
    Insn &cmpInsn = (cgFunc->GetInsnBuilder()->BuildInsn(cmpMOp, X64CG::kMd[cmpMOp]));
    cmpInsn.AddOperandChain(regOpnd1).AddOperandChain(regOpnd0);
    cgFunc->GetCurBB()->AppendInsn(cmpInsn);
  }
  /* set result -> u8 */
  CGRegOperand &tmpResOpnd = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, cgFunc->GetRegTyFromPrimTy(PTY_u8));
  x64::X64MOP_t setMOp = GetSetCCMop(opCode, tmpResOpnd.GetKind(), isSigned);
  ASSERT(setMOp != x64::MOP_begin, "unsupported mOp");
  Insn &setInsn = cgFunc->GetInsnBuilder()->BuildInsn(setMOp, X64CG::kMd[setMOp]);
  setInsn.AddOperandChain(tmpResOpnd);
  cgFunc->GetCurBB()->AppendInsn(setInsn);
  /* cvt u8 -> primType */
  SelectIntCvt(resOpnd, tmpResOpnd, primType);
}

}
